﻿using System;
using System.Xml;
using Notoric.Model.Internal;
using Notoric.Model.Packaging;

namespace Notoric.Model.Elements
{
    /// <summary>
    /// Represents an attachment symbol that has an explicit end given
    /// by a length given in 128-th <see cref="Beats"/>.
    /// </summary>
    public abstract class BlockMarkElement : AttachmentElement
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="BlockMarkElement"/> class.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="CompositionPart"/>
        /// </para>
        /// <para>
        /// The package part that owns this element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="BeatElement"/>
        /// </para>
        /// <para>
        /// The beat that directly owns this element.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="packagePart"/> or <paramref name="owner"/> is <see langword="null"/>.
        /// </exception>
        protected BlockMarkElement(CompositionPart packagePart, BeatElement owner)
            : base(packagePart, owner)
        {
            Beats = 0;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BlockMarkElement"/> class.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="CompositionPart"/>
        /// </para>
        /// <para>
        /// The package part that owns this element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="BeatElement"/>
        /// </para>
        /// <para>
        /// The beat that directly owns this element.
        /// </para>
        /// </param>
        /// <param name="element">
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The XML element that holds the data of this object.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        protected BlockMarkElement(CompositionPart packagePart, BeatElement owner, XmlElement element)
            : base(packagePart, owner, element)
        {
            beatsAttribute = element.Attributes[PackageVocabulary.BeatsAttribute];
        }

        #endregion

        #region AttachmentElement Members

        /// <inheritdoc/>
        protected override XmlElement CreateObjectElementCore(XmlDocument document)
        {
            var element = CreateSymbolElement(document);

            beatsAttribute = document.CreateAttribute(PackageVocabulary.BeatsAttribute);

            element.Attributes.Append(beatsAttribute);

            return element;
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Creates the XML element that will hold the data of this symbol.
        /// </summary>
        /// <remarks>
        /// The attributes for inherited properties as well as the type information will be added
        /// by the base classes.
        /// </remarks>
        /// <param name="document">
        /// <para>
        /// Type: <see cref="XmlDocument"/>
        /// </para>
        /// <para>
        /// The document that will contain the element.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The created element.
        /// </para>
        /// </returns>
        protected abstract XmlElement CreateSymbolElement(XmlDocument document);

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the length of this block mark.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="UInt32"/>
        /// </para>
        /// <para>
        /// The length of this block mark.
        /// The length is given as a multiple of 128-th beats 
        /// (e.g. a block that is 1 beat long has <see cref="Beats"/> equal to 32 (128-th) beats).
        /// </para>
        /// </value>
        public uint Beats
        {
            get { return uint.Parse(beatsAttribute.Value, PackageVocabulary.PackageCulture.NumberFormat); }
            set
            {
                beatsAttribute.Value = value.ToString(PackageVocabulary.PackageCulture.NumberFormat);

                RaisePropertyChanged("Beats");
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="Beats"/> property.
        /// </summary>
        private XmlAttribute beatsAttribute;

        #endregion
    }
}
